<p>在Django中，Model是用户数据的抽象。Model保存了操作用户数据所必须包含的一些属性和方法。通俗的讲，Model是一张数据库表的映射</p>
<ul>
    <li>每个Model都是继承了<code>django.db.models.Model</code>的python类</li>
    <li>Model的属性代表了对应数据库表的一个字段(数据库表的某一列)</li>
    <li>Django会为Model自动生成访问数据库的API</li>
</ul>
<h4>一个小例子</h4>
<p>下面这个Model定义了一个Person，它有first_name和last_name两个属性</p>
<pre>
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
</pre>
<p>first_name和last_name是Person这个Model的字段(field)，每一个都是以类属性的方式定义的，每一个都对应于数据库中的一列。上面的Person这个Model在自动创建数据库表时会使用如下的sql语句</p>
<pre>
CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);
</pre>
<p>注意事项:</p>
<ul>
    <li>创建的数据库表名字为myapp_person, 这是由app名和Model名的小写形式拼接得来的，这是Django的默认行为，Django允许自定义表名的生成规则。在Meta属性那一章节会讲到</li>
    <li>id字段是自动添加的一个主键，这一行为也可以自定义</li>
</ul>
<h4>使用Model</h4>
<p>定义好Model以后并不是可以直接使用的，我们要告诉Django我们要使用哪个model，也就是把Model激活。方法是把包含这个Model的app的app名写入INSTALLED_APPS这个配置项(该配置项位于settings.py中)</p>
<p>例如:我们在myapp.models(myapp/models.py)这个模块里面定义了Person这个Model，那么INSTALLED_APPS应该看起来像这样</p>
<pre>
INSTALLED_APPS = [
    #...
    'myapp',
    #...
]
</pre>
<p>往INSTALLED_APPS里面添加完新的app后，运行python3 manage.py migrate来完成对数据库的变更(包括新建数据库表，修改数据库表，删除表等)</p>
<h4>Field</h4>
<p>一个Model最核心也是唯一必须写的地方在于这个Model里面定义的这些Field。Field以类属性的方式定义。在给Field起名字的时候要注意不要与Django的保留字冲突，例如clean，save，delete这些都是Django的数据库API用到的属性名</p>
<p>一个例子:</p>
<pre>
from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()
</pre>
<p>Model的每个field字段都是一个Field类的实例，Django会根据Field类来确定以下几样东西</p>
<ul>
    <li>数据库表的对应列的数据类型(例如，INTEGER, VARCHAR, TEXT)</li>
    <li>使用form自动生成html表单的html组件(样式)，Django会根据field来确定使用哪种组件(例如:<code><span class="pre">&lt;input</span><span class="pre">type="text"&gt;</span>, <span class="pre">&lt;select&gt;</span></code>), 后面的章节会讲到</li>
    <li>确定每个field对应的最基础的数据验证机制，例如first_name的长度不能超过50等等</li>
</ul>
<h4>Field类的属性</h4>
<p>每一个Field类都有其特定的属性集(参数集)(只在这一个Field或者某几个Field中存在), 例如Charfield需要max_length这个参数来确定其对应的VARCHAR字符串的最大长度是多少。除了这些Field特定的参数外，还有一些通用的参数，这些参数是可选的。下面来介绍一下最常用的几个。更详细的介绍会在field那一章节来介绍</p>
<p>null</p>
<p>默认值为False，如果设为True，Django遇到空值时会在其对应的数据库列里面存NULL</p>

<p>blank</p>
<p>默认值为False，如果设置为True，那么Django允许该field为空</p>
<p>注意:blank和null是不同的。null是数据库层面的，blank是Django层面的，Django的数据验证机制会使用到blank这个参数。如果blank为True，那么在表单验证的时候，model里对应的blank为True的field就允许设为空，否则就必须设定一个值</p>

<p>choices</p>
<p>这个参数为tuple类型，其中的每一个元素也必须为tuple类型并且作为元素的tuple的长度必须为2。它默认的html组件是<code><span>select</span></code>。看下面的例子:</p>
<pre>
YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)
</pre>
<p>每一个子tuple的第一个元素(例如FR)是实际存在数据库里的值，第二个值(例如Freshman)是在使用Django的form组件时展示在页面上的值。给定一个Model的实例对象，它对应的choices类型的field的显示值可以使用<code>get_FOO_display()</code>这个方法来拿到。下面是一个例子:</p>
<pre>
from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
</pre>
<pre>
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
</pre>
<p>default</p>
<p>该field的默认值，在创建Model实例的时候，如果某些field没有初始化，那么设定了default的field就会获得一个默认值。这个属性可以是一个确切的值也可以是一个方法(callable)，如果是方法的话，每次创建新的Model实例都会调用该方法以获取一个初始值</p>
<p>primary_key</p>
<p>如果设为True，那么这个field对应的列就是数据库表的主键。如果在定义Model时没有设置了primary_key=True的field，那么Django会自动生成一个自增的整数类型的字段作为主键。 因此，不必每次都设置primary_key。注意，设置了primary_key=True的field是只读(readonly)的，不能修改它的值。修改主键的值等于新建了一条记录(一个Model实例)。一个例子:</p>
<pre>
from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
</pre>
<pre>
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
['Apple', 'Pear']  # 我们只创建了一个fruit实例，数据库却有2条记录
</pre>
<p>unique</p>
<p>这个参数用来控制数据库表的列是否具有唯一这个特性</p>
<h4>自动主键</h4>
<p>上面提到，在没有定义主键的情况下，Django会为Model自动添加一个自增的主键。这个主键是这样的</p>
<pre>
id = models.AutoField(primary_key=True)
</pre>
<p>Django要求每个model都有一个field设置primary_key=True, 如果我们没有设置，Django就会添加上这个主键，如果我们已经设置了，那么Django就不会添加上面这个了</p>
<h4>关联关系</h4>
<p>多对一(ForeignKey)</p>
<p>在Django里面，我们使用<code>django.db.models.ForeignKey</code>来定义多对一的关系，它对应于SQL中的外键。它的使用方法跟定义普通的field一致。需要注意的是，ForeignKey有一个必填的位置参数(positional argument), 这个参数用来表示这个多对一关系是关联到哪个Model，第二个需要注意的是，多对一关系要定义在多的一方的Model里。举个例子：现有Car和Manufacturer两个Model, 每个制造商可以生产多辆车，但是每一辆车只能有一个生产厂家，这就是一个多对一的关系，反应到Model上是这样的:</p>
<pre>
from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...manufacturer这个field实例的名利可以随意修改,比如修改成
    # company_that_makes_it = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # 但是建议使用Model名字的小写形式
</pre>
<p>Django官方提供了非常详细的示例代码，请参考<a href="https://docs.djangoproject.com/en/1.10/topics/db/examples/many_to_one/">many_to_one_example</a></p>
<p>多对多关系</p>
<p>Django使用<code>django.db.models.ManyToManyField</code>来定义多对多关系，使用方法同ForeignKey一致。多对多关系也有一个必填的位置参数(positional argument)来表示这个关系关联到的Model, 另外不同于ForeignKey只能定义在'多'的一方(只能定义在many-to-one的many那边, 由many指向one), 多对多关系可以定义在任何一方的Model里(多对多关系的指向可以任意)。举个例子:一篇论文可以有多个作者，一个作者可以有多篇论文，这个关系表现在Model上是这样的</p>
<pre>
from django.db import models

class Article(models.Model):
    # ...
    pass

class Author(models.Model):
    # ...
    articles = models.ManyToManyField(Article)
</pre>
<p>Django官方提供了非常详细的示例代码，请参考<a href="https://docs.djangoproject.com/en/1.10/topics/db/examples/many_to_many/">many_to_one_example</a></p>
<p>多对多进阶</p>
<p>上面讲到的只是非常简单的多对多关系。实际应用中，有些场景可能更复杂，这种简单的多对多关系需要额外的设置才能胜任。考虑音乐家和乐团的关系。一个乐团有多个音乐家，一个音乐家也可能同时属于多个乐团，假设我们追踪这个多对多关系的同时需要追踪哪个音乐家在什么时间加入了哪个乐团这样的信息，我们就需要用到这些额外的设置了。为了应对这样的问题，ManyToManyField提供一个<code>through</code>参数来指定一个额外的Model来管理多对多关系</p>
<pre>
from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')  # 定义多对多关系，由Group指向Person

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
</pre>
<p>上面的例子，我们加多了一个充当中间人角色的Membership来管理多对多的关系。这个中间人Model定义了两个多对多关系的Model是如何关联的</p>
<p>要定义一个这种多对多的中间Model，需要遵循以下约定</p>
<ul>
    <li>作为中间Model的这个Model必须包含指向两个构成多对多关系的Model(Person和Group)的外键, 并且指向多对多关系的目标Model(Person)和源Model(Group)的外键只能有一个(Membership中只能有切只有一个指向Person的外键，有且只能有一个指向Group的外键)。另外，如果Membership有超过1个指向源Model(Group)的外键，那么需要给ManyToManyField传递一个额外的through_fields来指定Membership中的哪个字段是用来管理多对多关系的。同理如果Membership有超过1个指向目标Model(Person)的外键，也一样需要through_fields这个参数。关于through_fields的使用，我们会在"Django进阶-Field"这一章节介绍</li>
    <li>指向自身的多对多关系，会出现2个指向同一个Model的外键，这样是可以的。但是，这两个外键字段会分别被当做多对多关系的源和目标。如果有超过2个指向自身的外键，那么就需要through_fields这个参数了</li>
    <li>在定义指向自身的多对多关系时，如果要使用中间Model，必须传递参数symmetrical=False, 具体参考"Django进阶-Field"</li>
</ul>
<p>下面是一些使用上面我们定义的多对多关系的代码示例</p>
<pre>
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()  # 反向查询，Django默认使用model名(group)加_set来进行反向的关系查询，后面会讲到如何自定义(使用related_name)
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
</pre>
<p>使用中间Model的多对多关系与普通的多对多关系不同的地方在于无法使用add(), create(), 或者set()来关联两个对象了。我们必须明确的定义多对多关系的所有细节。</p>
<pre>
>>> # 对于普通的多对多关系，下面的代码是正常工作的。使用了中间Model的多对多关系，下面的代码不工作
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])
</pre>
<p>这是为什么呢？因为，使用中间Model(比如Membership)定义多对多关系的方式需要我们提供多对多关系的额外信息(上例中的date_joind和invite_reason)，但是add, create()这些方法并没有提供供我们指定这些额外信息的参数，因此这些方法在这种情况下都被停用了。同理，remove()方法也被停用了，但是clear()方法还是可以用来清除一个对象所有的多对多关联的</p>
<pre>
>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # 下面这行代码不工作，因为Django无法判断要删除哪一条Membership记录
>>> beatles.members.remove(ringo)
</pre>
<pre>
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # 上面这行代码会把beatles关联的所有Membership都删除
>>> Membership.objects.all()
<QuerySet []>
</pre>
<p>api示例</p>
<pre>
# Find all the groups with a member whose name starts with 'Paul'
# 查询包含名字为Paul的成员的Group
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

# Find all the members of the Beatles that joined after 1 Jan 1961
# 查询所有在1961年1月1日以后加入Beatles的人
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

# 直接查询Membership的信息
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

# 从Person查询Membership
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
</pre>
<h4>一对一关系(one-to-one)</h4>
<p>使用OneToOneField来定义一对一关系，其使用方式与定义其他field相同。一对一关系多用在扩展一个Model的场景下。同多ForeignKey一样，OneToOneField也有一个必填的位置参数(positional argument)来表明是与哪个Model建立一对一关系。举个例子:一个餐馆(Restaurant)只会有一个地址(Address)，一个地址上也只能有一个餐馆</p>
<pre>
from django.db import models

class Address(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Restaurant(models.Model):
    name = models.CharField(max_length=128)
    members = models.OneToOneField(Address)  # 定义一对一关系， 有Restaurant指向Address

    def __str__(self):              # __unicode__ on Python 2
        return self.name
</pre>
<h4>跨文件定义关系</h4>
<p>Django允许使用不在同一个model文件里定义的Model来定义一对一，多对多或者多对一的关系。举例:</p>
<pre>
from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )
</pre>
<h4>Field起名限制</h4>
<p>在给Model的各个Field起名时，Django有2个限制条件，分别是</p>
<ul>
    <li>field名字不能是python的保留字</li>
    <li>field名字不能含有2个或2个以上的下划线</li>
    <pre>
class Example(models.Model):
    foo__bar = models.IntegerField() # 'foo__bar' 有2个下划线，这是不允许的
    </pre>
</ul>
<h4>Meta属性</h4>
<p>使用<code>class Meta</code>来给Model类添加Meta属性。</p>
<pre>
from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"
</pre>
<p>Meta属性是用来控制Model里面与Field无关的属性的接口。例如可以通过Meta来控制数据库的默认排序方式，Model对应的数据库表等等。有关Meta的详细的内容将会在Meta属性那一章节讲解</p>
<h4>Model属性</h4>
<p>每一个Model类都会有个Manager类型的类属性，默认情况下该属性的名字为objects。该属性是Django提供的访问数据库的接口并且只能通过类名访问，不能使用实例访问。Django允许自定义Manager，后面的章节再来讲</p>
<h4>Model方法</h4>
<p>Django允许在Model上定义方法来完成对数据的进一步处理。在Model上定义方法跟在普通的python类上面定义方法一样</p>
<pre>
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)
</pre>
<h4>重写(覆盖)model方法</h4>
<p>django会给每个Model添加许多预先定义好的方法，例如save,delete()。这些方法的行为是django定义的。如果我们想修改它们的行为，可以通过重写这个方法来实现。例如,下面的例子重写了save方法</p>
<pre>
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()
</pre>
<pre>
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
</pre>
<p>需要注意的是，在重写这些方法的时候，一定要调用一下父类(superclass)的save方法，要不然我们对数据做的修改就不会保存到数据库。另外一个需要注意的地方是,一定要有*args和**kwargs这两个参数，这是为了兼容Django的扩展(django会通过这些参数来扩展一些功能，加上这两个方法有助于实现更完备的功能)</p>
<h4>执行SQL</h4>
<p>Django支持直接运行SQL的查询，有关信息将在执行SQL那一章讲解</p>
<h4>Model继承</h4>
<p>Django中的model继承与python的类继承基本一致，唯一的区别在于我们是否希望作为父类的model作为一个单独的model存在(是否有独立的数据表)。Model继承有3种形式:</p>
<ul>
    <li>第一种:父类只是一个数据集的抽象(把出现在子类中的公共属性聚集在一起)，它不具有单独的数据库表，也不能在代码中使用。我们称之为抽象类继承(abstract base class)</li>
    <li>第二种:父类和子类分别拥有单独的数据库表，我们称为多表继承(multi-table inheritance)</li>
    <li>第三章:子类只修改了父类的某些python层面的行为，并没有对父类的field进行修改，例如添加了一个方法或者重写了一个方法，这种称为代理继承(Proxy model)</li>
</ul>
<p>抽象类继承(Abstract base class)</p>
<p>抽象类继承在许多model拥有相同字段的场景下是很有用的。它可以减少我们的编码量。通过为Model设置<code>abstract=True</code>的属性来把一个类变成抽象类。Django不会为抽象类创建数据库表。另外需要注意的是，这种继承方式下，父类和子类不能有同名的属性(field)。例:</p>
<pre>
from django.db import models

class CommonInfo(models.Model):  # 无数据库表
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True  # 注意

class Student(CommonInfo):  # 有数据库表
    home_group = models.CharField(max_length=5)
</pre>
<p>上例中，Student有name, age,home_group三个属性(field), CommonInfo这个Model不能单独使用，因为<code>abstract=True</code>, Django没有为其创建数据库的表，也没有为其生成数据库访问的API。</p>
<p>Meta的继承</p>
<p>抽象类这种继承模式下，子类的Meta会完全继承父类的Meta。如果子类想扩展父类的Meta，可以通过继承父类Meta的方式。例:</p>
<pre>
from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'
</pre>
<p>前面说到，子类会完全继承父类的Meta，但是实际上，Django还是有对子类的Meta做一个修改的，这也是Django对子类的Meta做的唯一的一处修改。这一处修改是这样子的，Django把子类Meta的abstract设置成了false，这样子类就不是抽象类了。如果想让子类也是抽象类，需要手动设置abstract=True。</p>
<p>related_name和related_query_name</p>
<p>前面的代码示例里面有提到反向查询，我们还是以Article和Author来说明一下</p>
<pre>
from django.db import models

class Article(models.Model):
    # ...
    pass

class Author(models.Model):
    # ...
    articles = models.ManyToManyField(Article)
</pre>
<p>默认情况下，从Author访问Article对象只需要通过Author自身的articles属性，反过来，通过Article访问Author对象，默认情况下是使用Article对象的author_set(model名的小写形式加_set)属性, 多对多和多对一两种关系都提供了related_name这个属性来方便反向查询。修改Author:</p>
<pre>
class Author(models.Model):
    # ...
    articles = models.ManyToManyField(Article, related_name="author")
</pre>
<p>上面的代码给ManyToManyField传了一个related_name参数，这样以后就可以通过articleobj.author这种方式来访问某个Article的Author关联对象了。Many-to-One也是同理。</p>
<p>如果把related_name用在抽象类的定义中时(抽象类中有ManyToManyField或者ForeignKey)，要特别注意, 因为抽象类的字段会被所有的子类继承，因此，如果我们还是原来一样写<code>related_name="author"</code>这样是不行的，因为每个子类(Model)都有一个相同related_name的字段，Django没办法分辨出我们要找的是哪个Model。Django提供了<code>'%(app_label)s'</code>和<code>'%(class)s'</code>来帮助解决这个问题</p>
<ul>
    <li>'%(class)s'会使用实际的Model(子类)的小写的名字来替换</li>
    <li>'%(app_label)s'会使用app的名字的小写形式来替换</li>
</ul>
<p>举个例子:名为commen的app的Model定义如下(commen/models.py)</p>
<pre>
from django.db import models

class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )

    class Meta:
        abstract = True

class ChildA(Base):
    pass

class ChildB(Base):
    pass
</pre>
<p>名为rare的app的Model定义如下(rare/models.py)</p>
<pre>
from common.models import Base

class ChildB(Base):
    pass
</pre>
<p><code>common.ChildA.m2m</code>的related_name为<code>common_childa_related</code>, <code>common.ChildB.m2m</code>的related_name为<code>common_childb_related</code>, 最后<code>rare.ChildB.m2m</code>的related_name为<code>rare_childb_related</code></p>
<h4>多表继承(Multi-table)</h4>
<p>接下来讲解多表继承的方式，这种方式下每个Model都有自己单独的数据库表。其写法跟普通的类继承一模一样。例子:</p>
<pre>
from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
</pre>
<p>上例中，Place的所有字段(属性，field)都会在Restaurant中出现，但是两个Model使用的是不同的数据库表，name和address在2张表中都会出现。</p>
<pre>
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
</pre>
<p>如果有对象既是Place, 又是Restaurant, 那么可以通过Place直接获取到Restaurant对象</p>
<pre>
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
</pre>
<p>注意，上面的代码的写法在p是Restaurant的时候可以，如果p不是一个Restaurant, 这么写会抛出<code>Restaurant.DoesNotExist</code>异常</p>
<p>多表继承下的Meta</p>
<p>在多表继承的模式下，父类的Meta完全不会被子类继承，注意是完全不会。但是有2个属性是特例，子类会从父类继承ordering和get_latest_by这2个Meta属性</p>
<p>多表继承与反向关系查询</p>
<p>前面提到，多表继承模式下，Django默认会添加一个由子类指向父类的OneToOneField, 默认的也会产生一个指向子类的反向关系查询的属性。还是采用Place和Restaurant的例子,默认的会有一个由Restaurant指向Place的一对一关系(place_ptr属性)，也会有一个默认的由Place指向Restaruant的反向关系查询的属性(类似多对一和多对多关系中的xxx_set)，如果我们在子类中再包含一个指向父类的多对一或者多对多关系，那么系统会报异常，就像下面这样</p>
<pre>
class Supplier(Place):
    customers = models.ManyToManyField(Place)
</pre>
<p>异常信息:</p>
<pre>
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
</pre>
<p>关于上面的这个异常我个人的理解: 默认情况下，Django添加的一对一关系的反向查询属性和多对多关系的反向查询属性，他们的名字是一样的，也就是，当Django发现已经有了从place到Supplier的反向属性的时候，后面新加的从Supplier指向Place的关系，其反向查询属性(字段)采用同前面的关系同样的属性，也就是，不管Supplier有多少指向Place的关系字段(包括OneToOneField, ForeignKey, ManyToManyField), Django都只会生成一个反向查询属性。因此，上面的例子中，由Place通过一对一关系(通过'Supplier.place_ptr'建立)查询Supplier的属性和由多对多关系(通过'Supplier.customers'建立)查询Supplier的属性是同一个，因此Django就没法区分是要查询多对多关系还是多对一关系，因此就报错了。解决办法是显示地添加related_name</p>
<pre>
class Supplier(Place):
    customers = models.ManyToManyField(Place, related_name='provider')
</pre>
<p>如果想改变默认的由子类指向父类的一对一关系的名字，可以在子类中显示定义一对一关系，并设置parent_link=True</p>
<pre>
class Supplier(Place):
    placexxx = models.OneToOneField(Place, parent_link=True)  # 可以通过supplier.placexxx来访问父类对象
    customers = models.ManyToManyField(Place, related_name='provider')
</pre>
<h4>代理继承(Proxy Model)</h4>
<p>所谓的代理继承就是给源Model(父类)创建了一个代理Model。通过这个代理Model我们可以创建，修改，删除数据，所有对数据的修改都会被存入源Model里面，就好像我们在操作源Model一样.不同点在于，通过代理Model，我们可以更改排序类型，默认的manager等等。要使用代理继承，只需要在子类的Meta中设置<code>proxy=True</code>。例:</p>
<pre>
from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass
</pre>
<p>上面的例子中，MyPerson和Person操作相同的数据库表</p>
<pre>
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
</pre>
<p>使用代理来自定义排序</p>
<pre>
class OrderedPerson(Person):  # 默认的查询将会依照last_name排序
    class Meta:
        ordering = ["last_name"]
        proxy = True
</pre>
<p>代理继承注意事项</p>
<ul>
    <li>使用代理继承模式，子类的Meta继承规则同多表继承。</li>
    <li>子类和父类不能互相替换使用</li>
    <li>子类只能继承自唯一的非抽象类(只能有一个不是抽象类的父类)</li>
    <li>子类可以继承自多个代理Model，这些作为父类的代理Model需要拥有一个共同的不是抽象类的父类Model</li>
    <li>子类可以继承自多个没有定义任何字段的抽象类Model</li>
</ul>
<p id="proxy-model-manager">给proxy Model自定义manager</p>
<p>默认情况下，如果没有给Proxy Model定义manager，那么Proxy Model会继承父类的objects，如果在Proxy Model中定义了manager，那么我们定义的这个manager就会变成默认的manager。</p>
<pre>
from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()  # 自定义的manager， MyPerson只有一个manager

    class Meta:
        proxy = True
</pre>
<p>上面的例子中MyPerson只有一个Manager, 如果我们想给这个Proxy Model添加额外的manager可以通过这样的方式：创建一个父类，这个父类为抽象类并且有一个自定义的manager，子类继承这个抽象的父类，例:</p>
<pre>
# 给新的manager创建一个抽象类.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):  # 这里运用到了多继承
    class Meta:
        proxy = True
</pre>
<p>代理Model与镜像Model的区别</p>
<p>上面讲到，代理模式的继承，两个类最后操作的是同一个数据库表，那么这种方式跟我们重写一个Model然后把db_table设为跟原来的Model一样这种方法有什么区别呢？就像这样</p>
<pre>
from django.db import models

class Person(models.Model):
    # ...
    name = models.CharField(max_length=15)

class MyPerson(Person):
    name = models.CharField(max_length=15)

    class Meta:
        db_table = 'app_person'  # 这里设置为跟Person同样的表明
</pre>
<p>这两中方式的区别在于，使用Proxy的方式，Django会自动的保持Proxy和源Model的数据保持同步，设置db_table的方式需要我们自己来保持两个Model之间的数据同步。例如我们修改了Person中的name，那么它对应的Proxy中的name也会同时修改，但是MyPerson的name属性需要我们手动修改</p>
<h4>多继承(Multiple inheritance)</h4>
<p>Model的多继承与python类的多继承机制是相同的。需要注意的是属性查询机制。在从父类搜索某一个名字时，例如Meta，搜索到第一个匹配的名字就停止搜索了。也就是说，如果有多个父类含有Meta，当搜索到第一个含有Meta的父类时就停止搜索了，当前找到的这个Meta就被应用。另外需要注意的是，多继承一定要给每个父类单独的指定主键(primary=True), 如果多个父类都是使用默认生成的id这个字段作为主键，那么会出错。例子:</p>
<pre>
class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...

class BookReview(Book, Article):
    pass
# 代码正常工作
</pre>
<p>或者使用抽象类来存储id</p>
<pre>
class Piece(models.Model):
    pass

class Article(Piece):
    ...

class Book(Piece):
    ...

class BookReview(Book, Article):
    pass
</pre>
<h4>Model的组织结构</h4>
<p>通常情况下，我们在app目录下的models.py中定义Model，如果我们有很多很多的Model要定义，最好还是重新组织一下代码结构。一个优化的方案是在app目录建立一个models目录<code>myapp/models/</code>，在这个目录的__init__.py中import所有的Model。假设有Person和Robot这2个Model:</p>
<em>myapp/models/__init__.py</em>
<pre>
from .organic import Person
from .synthetic import Robot
</pre>
<h4>其他注意事项</h4>
<p>Django不允许名字隐藏(只对field类型的属性有效,其他属性允许名字隐藏)，任何非抽象类的父类Model不能与子类Model有相同的字段(field)名字, 抽象的父类Model是可以的，但是尽量不用</p>
